<!--
 * @FilePath: /my-map-demo/src/views/spatial-analysis/BufferAnalysis.vue
 * @Author: 张文鑫
 * @Date: 2024-12-30 11:23:47
 * @Description: 缓冲区分析组件
-->
<template>
  <div class="buffer-analysis-container">
    <div id="map" class="map-container" v-loading="loading" element-loading-text="正在分析中..."></div>
    <div class="buffer-analysis">
      <el-card class="analysis-panel">
        <template #header>
          <div class="card-header">
            <span>缓冲区分析示例</span>
          </div>
        </template>

        <el-form :model="formData" label-width="100px">
          <el-form-item label="分析对象">
            <el-select v-model="formData.selectedPOI" placeholder="选择分析对象">
              <el-option label="西湖商圈" value="xihu" />
              <el-option label="武林商圈" value="wulin" />
              <el-option label="钱江新城商圈" value="qianjiang" />
              <el-option label="滨江商圈" value="binjiang" />
              <el-option label="自定义位置" value="custom" />
            </el-select>
          </el-form-item>

          <el-form-item label="要素类型" v-if="formData.selectedPOI === 'custom'">
            <el-select v-model="formData.featureType" placeholder="选择要素类型">
              <el-option label="点" value="point" />
              <el-option label="线" value="line" />
              <el-option label="面" value="polygon" />
            </el-select>
          </el-form-item>

          <el-form-item label="缓冲距离">
            <el-input-number
              v-model="formData.bufferDistance"
              :min="100"
              :max="5000"
              :step="100"
              controls-position="right"
            >
              <template #suffix>米</template>
            </el-input-number>
          </el-form-item>

          <div class="button-container">
            <el-button-group>
              <el-button type="primary" @click="startDraw" :disabled="formData.selectedPOI !== 'custom'">
                <el-icon><Edit /></el-icon>
                绘制位置
              </el-button>
              <el-button type="success" @click="performBufferAnalysis" :disabled="!canAnalyze"> 执行分析 </el-button>
              <el-button type="warning" @click="clearAnalysis"> 清除分析 </el-button>
            </el-button-group>
          </div>

          <div class="analysis-result" v-if="analysisResult">
            <el-divider>分析结果</el-divider>
            <p>分析范围：{{ formData.bufferDistance }}米</p>
            <p>找到建筑物：{{ analysisResult.buildingCount }} 个</p>
            <p>居住区面积：{{ analysisResult.residentialArea }} 公顷</p>
          </div>
        </el-form>
      </el-card>
    </div>
  </div>
</template>

<script setup>
import { reactive, ref, computed, onMounted, onUnmounted, nextTick, watch } from "vue";
import { Edit } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import { Vector as VectorSource } from "ol/source";
import { Vector as VectorLayer } from "ol/layer";
import { Draw } from "ol/interaction";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import Feature from "ol/Feature";
import { Point, LineString, Polygon, Circle } from "ol/geom";
import * as turf from "@turf/turf";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import OSM from "ol/source/OSM";
import { fromLonLat, transformExtent, transform } from "ol/proj";

// 创建一个 ref 来存储地图实例
const mapInstance = ref(null);

// 预设的POI点位
const POILocations = {
  xihu: [120.1552, 30.2417], // 西湖商圈（湖滨银泰周边）
  wulin: [120.1699, 30.2659], // 武林商圈（武林广场）
  qianjiang: [120.2092, 30.2588], // 钱江新城商圈（市民中心）
  binjiang: [120.2122, 30.2089], // 滨江商圈（星光大道）
};

const formData = reactive({
  selectedPOI: "xihu", // 默认选中西湖商圈
  bufferDistance: 1000,
  featureType: "point",
});

// 将 drawSource 等变量改为 ref，以便更好地追踪状态
const drawSource = ref(null);
const drawLayer = ref(null);
const bufferSource = ref(null);
const bufferLayer = ref(null);
const draw = ref(null);
const selectedFeature = ref(null);

// 添加一个 ref 存储底图图层
const baseLayer = ref(null);

// 添加一个存储居民区图层的 ref
const residentialLayer = ref(null);
const residentialSource = ref(null);

// 添加分析结果状态
const analysisResult = ref(null);

// 添加 loading ref
const loading = ref(false);

// 添加初始化预设点位的函数
const initializeSelectedPOI = () => {
  if (formData.selectedPOI !== "custom" && POILocations[formData.selectedPOI]) {
    const coords = fromLonLat(POILocations[formData.selectedPOI]);
    const point = new Feature({
      geometry: new Point(coords),
      name: formData.selectedPOI,
    });

    // 清除现有要素并添加新要素
    drawSource.value.clear();
    bufferSource.value.clear();
    residentialSource.value?.clear();

    drawSource.value.addFeature(point);
    selectedFeature.value = point;

    // 调整视图到选中的POI
    mapInstance.value.getView().animate({
      center: coords,
      zoom: 15,
      duration: 1000,
    });
  }
};

// 修改 canAnalyze 的计算逻辑
const canAnalyze = computed(() => {
  if (formData.selectedPOI === "custom") {
    return drawSource.value && drawSource.value.getFeatures().length > 0;
  }
  // 对于预设POI，确保要素已经创建
  return selectedFeature.value !== null;
});

// 修改 watch 函数
watch(
  () => formData.selectedPOI,
  (newValue) => {
    clearAnalysis();
    if (newValue !== "custom") {
      initializeSelectedPOI();
    }
  }
);

// 修改 startDraw 函数
const startDraw = () => {
  clearAnalysis();

  if (draw.value) {
    mapInstance.value.removeInteraction(draw.value);
  }

  let geometryType;
  switch (formData.featureType) {
    case "point":
      geometryType = "Point";
      break;
    case "line":
      geometryType = "LineString";
      break;
    case "polygon":
      geometryType = "Polygon";
      break;
    default:
      geometryType = "Point";
  }

  draw.value = new Draw({
    source: drawSource.value,
    type: geometryType,
  });

  mapInstance.value.addInteraction(draw.value);

  // 添加绘制开始事件监听
  draw.value.on("drawstart", () => {
    // 清除之前的要素
    drawSource.value.clear();
    bufferSource.value.clear();
  });

  draw.value.on("drawend", (event) => {
    selectedFeature.value = event.feature;
    mapInstance.value.removeInteraction(draw.value);
    ElMessage.success("绘制完成，可以进行缓冲区分析");
  });
};

// 修改 clearAnalysis 函数
const clearAnalysis = () => {
  // 清除图层数据
  [drawSource, bufferSource, residentialSource].forEach((source) => {
    source.value?.clear();
  });

  // 清除交互和选中状态
  selectedFeature.value = null;
  if (draw.value) {
    mapInstance.value.removeInteraction(draw.value);
  }

  // 重置分析结果
  analysisResult.value = null;

  // 如果是预设POI，重新创建点位
  if (formData.selectedPOI !== "custom") {
    initializeSelectedPOI();
  }

  // 重置视图
  const view = mapInstance.value.getView();
  view.animate({
    zoom: 12,
    duration: 1000,
  });
};

// 修改计算面积的函数
const calculateArea = (feature) => {
  try {
    // 直接使用 feature 的几何对象
    const geometry = feature.get("geometry");
    const flatCoords = geometry.getFlatCoordinates();
    const ends = geometry.getEnds();

    // 构建坐标数组
    const coords = [];
    let offset = 0;
    for (let i = 0; i < ends[0]; i += 2) {
      coords.push([flatCoords[i], flatCoords[i + 1]]);
    }

    // 转换坐标
    const transformedCoords = coords.map((coord) => transform(coord, "EPSG:3857", "EPSG:4326"));

    // 创建 GeoJSON 格式的多边形
    const polygon = {
      type: "Feature",
      properties: {},
      geometry: {
        type: "Polygon",
        coordinates: [transformedCoords],
      },
    };
    // 计算面积
    const area = turf.area(polygon);
    return area;
  } catch (error) {
    console.error("计算面积错误:", error);
    return 0;
  }
};

// 修改计算结果的函数
const calculateResults = (features) => {
  let buildingCount = 0;
  let totalArea = 0;

  features.forEach((feature) => {
    // 获取属性
    const properties = feature.getProperties();
    const tags = properties.properties || {}; // 获取 tags 对象

    // 检查建筑物类型和土地用途
    const building = tags.building;
    const landuse = tags.landuse;

    // 计数建筑物
    if (building) {
      buildingCount++;

      // 计算居住区面积
      if (
        landuse === "residential" ||
        building === "residential" ||
        building === "apartments" ||
        building === "house" ||
        building === "dormitory" ||
        building === "yes"
      ) {
        const area = calculateArea(feature);
        // console.log("Building type:", building, "Area:", area);
        totalArea += area;
      }
    }
  });

  // 转换为公顷（hectares）并保留两位小数
  const residentialArea = +(totalArea / 10000).toFixed(2);

  console.log("统计结果:", {
    buildingCount,
    residentialArea: `${residentialArea} 公顷`,
    totalArea,
  });

  return {
    buildingCount,
    residentialArea,
  };
};

// 修改 performBufferAnalysis 函数
const performBufferAnalysis = async () => {
  if (!selectedFeature.value) {
    ElMessage.warning(formData.selectedPOI === "custom" ? "请先绘制要素" : "未找到选中的商圈位置，请重新选择");
    return;
  }

  try {
    // 开启 loading
    loading.value = true;
    ElMessage.info("正在分析商圈周边...");

    const geometry = selectedFeature.value.getGeometry();
    const bufferDistance = formData.bufferDistance;

    let bufferGeom;
    if (geometry instanceof Point) {
      // 点的处理保持不变
      const coords = geometry.getCoordinates();
      const lonLat = transform(coords, "EPSG:3857", "EPSG:4326");
      const point = {
        type: "Point",
        coordinates: lonLat,
      };
      const buffered = turf.buffer(point, bufferDistance / 1000, { units: "kilometers" });
      bufferGeom = new Polygon(buffered.geometry.coordinates).transform("EPSG:4326", "EPSG:3857");
    } else if (geometry instanceof LineString) {
      // 线的处理
      const coords = geometry.getCoordinates();
      const transformedCoords = coords.map((coord) => transform(coord, "EPSG:3857", "EPSG:4326"));
      const line = {
        type: "LineString",
        coordinates: transformedCoords,
      };
      const buffered = turf.buffer(line, bufferDistance / 1000, { units: "kilometers" });
      bufferGeom = new Polygon(buffered.geometry.coordinates).transform("EPSG:4326", "EPSG:3857");
    } else {
      // 多边形的处理
      const coords = geometry.getCoordinates();
      // 对多边形的每个环进行坐标转换
      const transformedCoords = coords.map((ring) => ring.map((coord) => transform(coord, "EPSG:3857", "EPSG:4326")));
      const polygon = {
        type: "Polygon",
        coordinates: transformedCoords,
      };
      const buffered = turf.buffer(polygon, bufferDistance / 1000, { units: "kilometers" });
      bufferGeom = new Polygon(buffered.geometry.coordinates).transform("EPSG:4326", "EPSG:3857");
    }

    const bufferFeature = new Feature({
      geometry: bufferGeom,
      name: "Buffer",
    });

    bufferSource.value.clear();
    bufferSource.value.addFeature(bufferFeature);

    // 获取缓冲区范围内的居民区
    const extent = bufferGeom.getExtent();
    let [minLon, minLat, maxLon, maxLat] = transformExtent(extent, "EPSG:3857", "EPSG:4326");

    // 确保坐标顺序正确
    [minLon, maxLon] = minLon > maxLon ? [maxLon, minLon] : [minLon, maxLon];
    [minLat, maxLat] = minLat > maxLat ? [maxLat, minLat] : [minLat, maxLat];

    // 修改查询语句
    const query = `
      [out:json][timeout:60];
      (
        way["building"](${minLat},${minLon},${maxLat},${maxLon});
        way["landuse"="residential"](${minLat},${minLon},${maxLat},${maxLon});
      );
      out body geom;
    `.trim();

    ElMessage.info("正在获取居民区数据...");

    const response = await fetch("https://overpass-api.de/api/interpreter", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: `data=${encodeURIComponent(query)}`,
    });

    if (!response.ok) {
      const errorText = await response.text();
      console.error("API 响应错误:", errorText);
      throw new Error(`获取居民区数据失败: ${response.statusText}`);
    }

    const data = await response.json();

    if (!data.elements || data.elements.length === 0) {
      ElMessage.warning("该区域内未找到建筑数据，请尝试：\n1. 缩小查询范围\n2. 选择其他区域");
      return;
    }

    // 清除之前的居民区数据
    residentialSource.value.clear();

    // 将 OSM 数据转换为 GeoJSON
    const features = data.elements
      .map((element) => {
        if (element.type === "way" && element.geometry) {
          try {
            const coordinates = element.geometry.map((point) => [point.lon, point.lat]);
            if (coordinates.length < 3) return null;

            // 确保多边形闭合
            if (
              coordinates[0][0] !== coordinates[coordinates.length - 1][0] ||
              coordinates[0][1] !== coordinates[coordinates.length - 1][1]
            ) {
              coordinates.push([...coordinates[0]]);
            }

            return new Feature({
              geometry: new Polygon([coordinates]).transform("EPSG:4326", "EPSG:3857"),
              properties: {
                ...element.tags,
                id: element.id,
              },
            });
          } catch (error) {
            console.warn("处理要素时出错:", error);
            return null;
          }
        }
        return null;
      })
      .filter((feature) => feature !== null);

    if (features.length === 0) {
      ElMessage.warning("无法处理获取到的居民区数据");
      return;
    }

    // 添加特征到图层
    residentialSource.value.addFeatures(features);

    // 计算结果
    const results = calculateResults(features);
    analysisResult.value = results;

    // 添加：自动缩放到缓冲区范围
    const view = mapInstance.value.getView();
    const bufferExtent = bufferGeom.getExtent();

    // 计算合适的缩放级别和中心点
    view.fit(bufferExtent, {
      padding: [50, 50, 50, 50], // 设置内边距，使缓冲区不会紧贴边缘
      duration: 1000, // 动画持续时间
      maxZoom: 16, // 限制最大缩放级别
    });

    ElMessage.success(
      `分析完成，在 ${formData.bufferDistance} 米范围内找到 ${results.buildingCount} 个建筑物，
      居住区面积 ${results.residentialArea} 公顷`
    );
  } catch (error) {
    console.error("分析过程出错:", error);
    ElMessage.error(error.message === "获取居民区数据失败" ? "获取数据失败，请稍后重试" : "分析过程出错，请重试");
  } finally {
    // 关闭 loading
    loading.value = false;
  }
};

// 组件卸载时清理
onUnmounted(() => {
  if (mapInstance.value) {
    if (draw.value) {
      mapInstance.value.removeInteraction(draw.value);
    }
    if (drawLayer.value) {
      mapInstance.value.removeLayer(drawLayer.value);
    }
    if (bufferLayer.value) {
      mapInstance.value.removeLayer(bufferLayer.value);
    }
    if (residentialLayer.value) {
      mapInstance.value.removeLayer(residentialLayer.value);
    }
    // 不要移除底图图层
    mapInstance.value = null;
  }
});

// 添加 onMounted 钩子
onMounted(() => {
  // 创建底图图层
  baseLayer.value = new TileLayer({
    source: new OSM(),
    zIndex: 0, // 确保底图在最底层
  });

  // 初始化其他图层
  drawSource.value = new VectorSource();
  drawLayer.value = new VectorLayer({
    source: drawSource.value,
    style: new Style({
      fill: new Fill({
        color: "rgba(255, 204, 51, 0.4)",
      }),
      stroke: new Stroke({
        color: "#FF9800",
        width: 3,
      }),
      image: new CircleStyle({
        radius: 7,
        fill: new Fill({
          color: "#FF9800",
        }),
        stroke: new Stroke({
          color: "#fff",
          width: 2,
        }),
      }),
    }),
    zIndex: 1,
  });

  bufferSource.value = new VectorSource();
  bufferLayer.value = new VectorLayer({
    source: bufferSource.value,
    style: new Style({
      fill: new Fill({
        color: "rgba(100, 200, 100, 0.3)",
      }),
      stroke: new Stroke({
        color: "#4CAF50",
        width: 2,
        lineDash: [5, 5],
      }),
    }),
    zIndex: 2,
  });

  residentialSource.value = new VectorSource();
  residentialLayer.value = new VectorLayer({
    source: residentialSource.value,
    style: new Style({
      fill: new Fill({
        color: "rgba(255, 140, 0, 0.4)",
      }),
      stroke: new Stroke({
        color: "#ff8c00",
        width: 1,
      }),
    }),
    zIndex: 3,
  });

  // 初始化地图，使用杭州的中心坐标
  mapInstance.value = new Map({
    target: "map",
    layers: [baseLayer.value, drawLayer.value, bufferLayer.value, residentialLayer.value],
    view: new View({
      center: fromLonLat([120.1552, 30.2417]), // 以西湖为中心
      zoom: 12,
      maxZoom: 18,
      minZoom: 2,
    }),
  });

  // 添加图层变化监听
  drawSource.value.on("addfeature", () => {
    nextTick();
  });

  // 初始化地图后，创建预设点位
  nextTick(() => {
    initializeSelectedPOI();
  });
});
</script>

<style scoped>
.buffer-analysis-container {
  width: 100%;
  height: 100vh;
  position: relative;
}

.map-container {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}

.buffer-analysis {
  padding: 15px;
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 1000;
}

.analysis-panel {
  max-width: 400px;
  background-color: rgba(255, 255, 255, 0.95);
}

.card-header {
  font-weight: bold;
}

.button-container {
  display: flex;
  justify-content: center;
  width: 100%;
}

:deep(.el-button-group) {
  display: flex;
  width: 100%;
}

:deep(.el-button-group .el-button) {
  flex: 1;
}

:deep(.el-form-item) {
  margin-bottom: 20px;
}

:deep(.el-form-item__content) {
  display: flex;
  justify-content: center;
  width: 100%;
}

:deep(.el-input-number),
:deep(.el-select) {
  width: 100%;
}

:deep(.el-form) {
  width: 100%;
}

.analysis-panel :deep(.el-card__body) {
  padding: 20px;
  width: 100%;
  box-sizing: border-box;
}

.analysis-result {
  margin-top: 20px;
  padding: 15px;
  background-color: #f5f7fa;
  border-radius: 4px;
}

.analysis-result p {
  margin: 8px 0;
  color: #606266;
}

.el-divider {
  margin: 16px 0;
}

/* 修改 loading 相关样式 */
:deep(.el-loading-mask) {
  z-index: 9999 !important;
}

:deep(.el-loading-spinner) {
  transform: scale(1.2);
}

:deep(.el-loading-spinner .el-loading-text) {
  font-size: 16px;
  color: #409eff;
  margin-top: 10px;
}

:deep(.el-loading-spinner .path) {
  stroke: #409eff;
}
</style>
